/**
*  \file sdstructmgr.c
*
*  \brief Storage Device Structure Manager
*
*  \author Michael J. Hohmann  <michael.hohmann@linde-mh.de>
*
*
*/

/*===============================================[ private includes  ]================================================*/
#include "sdstructmgr.h"
#include "../assertor/assertor.h"
#include "../pal/pal_mem.h"
#include "../cfg/sdstructmgr_cfg.h"

/*===============================================[ private defines   ]================================================*/

/* magic numbers to indicate that an entry is valid */
#define SDSTRUCTMGR_A1   U8_CONST(0x5A)
#define SDSTRUCTMGR_A2   U8_CONST(0xA5)

#define SDSTRUCTMGR_B1   U8_CONST(0x55)
#define SDSTRUCTMGR_B2   U8_CONST(0xAA)

/*===============================================[ private datatypes ]================================================*/

/*===============================================[ private variables ]================================================*/

/*===============================================[ private functions ]================================================*/
static BO dataset_is_valid( struct sdstructmgr_management const * mgr_buffer);
static U16 sdstructmgr_stuct_cksum( U32 counter, void const* data, pal_size_t count );

/*================================================[ inline functions ]================================================*/

/*================================================[ public functions ]================================================*/

RC sdstructmgr_construct(
    struct sdstructmgr_obj *me,
    struct sd *sdev,
    struct sdstructmgr_management* management_adr,
	void  * dataset_adr,
    pal_size_t dataset_size,
	pal_size_t dataset_count
)
{
   ASSERTOR_PRE( NIL != me,"NIL is not allowed as object pointer." );
   ASSERTOR_PRE( NIL != sdev,"NIL is not allowed as sdev pointer." );
   ASSERTOR_PRE( 0   != dataset_count,"Zero is not allowed as numcnt." );
   {
	   pal_memzero(me, sizeof *me );

	   me->managment_adr=management_adr;
	   me->sdev=sdev;
	   me->dataset_adr=dataset_adr;
	   me->dataset_size=dataset_size;
	   me->dataset_count =dataset_count;
   }
   return RC_SUCCESS;
}

RC sdstructmgr_get( struct sdstructmgr_obj *me, void * dataset )
{
   ASSERTOR_PRE( NIL != me,"NIL is not allowed as object pointer." );
   ASSERTOR_PRE( NIL != dataset,"NIL is not allowed as dataset pointer." );
   {
	   RC rv=RC_ERROR;
	   U32 max_counter=0;
	   BO found_valid_dataset=FALSE;
	   BO reload_data=FALSE;
	   {
		   pal_size_t index=0;
		   U8 *dataset_adr=(U8*) me->dataset_adr;

		   for( index=0; index < me->dataset_count; index++)
		   {
			   struct sdstructmgr_management mgr_buffer;
			   sd_read( me->sdev, &mgr_buffer, &me->managment_adr[index], sizeof(struct sdstructmgr_management) );

			   if( dataset_is_valid( &mgr_buffer ) )
			   {
				   if(  mgr_buffer.count >=max_counter  )
				   {
					   sd_read( me->sdev, dataset, dataset_adr, me->dataset_size );

					   if( mgr_buffer.cks == sdstructmgr_stuct_cksum(mgr_buffer.count,dataset, me->dataset_size  ))
					   {
						   me->maxcount = mgr_buffer.count;
						   max_counter  = mgr_buffer.count;
						   found_valid_dataset=TRUE;
						   me->valid_index=index;
						   rv = RC_SUCCESS;
					   }
					   else
					   {
						   reload_data=TRUE; /* we read a dataset to the buffer which was not valid - so we have to read the valid one later again */
						   rv=RC_ERROR;
					   }
				   }
			   }
			   dataset_adr+=me->dataset_size;
		   }
	   }

	   if( found_valid_dataset && reload_data )
	   {
		   struct sdstructmgr_management mgr_buffer;
		   sd_read( me->sdev, &mgr_buffer, &me->managment_adr[me->valid_index], sizeof(struct sdstructmgr_management) );

		   if( dataset_is_valid( &mgr_buffer ) )
		   {
			   sd_read( me->sdev, dataset, me->dataset_adr+(me->valid_index * me->dataset_size), me->dataset_size );

			   if( mgr_buffer.cks == sdstructmgr_stuct_cksum(mgr_buffer.count,dataset, me->dataset_size  ))
			   {
				   rv=RC_SUCCESS;
			   }
			   else
			   {
				   rv=RC_ERROR;
			   }
		   }
		   else
		   {
			   rv=RC_ERROR;
		   }
	   }
	   return rv;
   }
}

void sdstructmgr_set( struct sdstructmgr_obj *me, void const* dataset )
{
   struct sdstructmgr_management mgr_buffer;
   struct sdstructmgr_management *p = NIL;
   pal_size_t new_index = 0;

   ASSERTOR_PRE( NIL != me,"NIL is not allowed as object pointer." );
   ASSERTOR_PRE( NIL != dataset,"NIL is not allowed as pointer to dataset." );

   { /* read the target buffer content */
 	   if( 0 == me->valid_index )
 	   {
 		   new_index=me->dataset_count-1;
 	   }
 	   else
 	   {
 		   new_index= me->valid_index-1;
 	   }
 	   p=&me->managment_adr[new_index];
 	   sd_read( me->sdev, &mgr_buffer, p , sizeof(struct sdstructmgr_management) );
   }

   {
	   U8 new_status[2];

	   if( mgr_buffer.status[0] == SDSTRUCTMGR_A1)
	   {
		   new_status[0]=SDSTRUCTMGR_A2;
		   new_status[1]=SDSTRUCTMGR_B2;
	   }
	   else
	   {
		   new_status[0]=SDSTRUCTMGR_A1;
		   new_status[1]=SDSTRUCTMGR_B1;
	   }

	   /* make struct invalid */
	   sd_write( me->sdev, &p->status[1],&new_status[1], sizeof( U8) );

	   /* write counter */
	   {
		   me->maxcount++;
		   sd_write( me->sdev, &p->count, &me->maxcount, sizeof( me->maxcount ) );
	   }
	   /* write chks */
	   {
		   U16 cks=sdstructmgr_stuct_cksum( me->maxcount, dataset, me->dataset_size );
		   sd_write( me->sdev, &p->cks, &cks, sizeof(cks) );
	   }

	   /* write data */
	   {
		   U8 *dataset_adr = me->dataset_adr+(new_index*me->dataset_size);
		   sd_write( me->sdev, dataset_adr, dataset, me->dataset_size );
	   }

	   /* make struct valid */
	   {
		   sd_write( me->sdev, &p->status[0],&new_status[0], sizeof(U8) );
	   }

	   /* set valid index so that we write the next data to the subsequent address */
	   me->valid_index=new_index;
	   //me->found_valid_struct=TRUE;
   }
}

/*===============================================[ private functions ]================================================*/
static BO dataset_is_valid( struct sdstructmgr_management const * mgr_buffer)
{
	BO rv=FALSE;

	if( mgr_buffer->status[0] == SDSTRUCTMGR_A1 )
	{
		if( mgr_buffer->status[1] == SDSTRUCTMGR_B1 )
		{
			rv=TRUE;
		}
		else
		{
			rv=FALSE;
		}
	}
	else
	{
		if( mgr_buffer->status[0] == SDSTRUCTMGR_A2 )
		{
			if( mgr_buffer->status[1] == SDSTRUCTMGR_B2 )
			{
				rv=TRUE;
			}
			else
			{
				rv=FALSE;
			}
		}
		else
		{
			rv=FALSE;
		}
	}

	return rv;
}



static U16 sdstructmgr_stuct_cksum( U32 counter, void const* data, pal_size_t count )
{
	U16 rv=sdstructmgr_cksum(0x0000,&counter,sizeof(counter) );
    rv=sdstructmgr_cksum(rv,data,count );
	return rv;
}

/***********************************************************************************************************************
*                             Copyright 2018 Linde Material Handling. All rights reserved.                             *
***********************************************************************************************************************/
